package com.ks.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ks.common.Comparator;
import com.ks.common.FormulaCalculator;
import com.ks.common.PageResult;
import com.ks.dto.PageQueryDTO;
import com.ks.entity.MetricData;
import com.ks.entity.Rule;
import com.ks.entity.WarnRecord;
import com.ks.exception.IllegalConfigurationException;
import com.ks.exception.NullValueException;
import com.ks.mapper.WarnRecordMapper;
import com.ks.service.WarnRecordService;
import com.ks.vo.RequestVO;
import com.ks.vo.WarnRecordVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WarnRecordServiceImpl extends ServiceImpl<WarnRecordMapper, WarnRecord> implements WarnRecordService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private WarnRecordMapper warnRecordMapper;

    @Value("${config.mode}")
    private String mode;

    private long offset;

    /**
     * 实时计算
     *
     * @param metricDataList 指标数据列表
     */
    @Override
    @Async
    public void currentCalculate(List<MetricData> metricDataList) {
        String ruleJson = stringRedisTemplate.opsForValue().get("rule");
        List<Rule> ruleList = JSON.parseArray(ruleJson, Rule.class);
        if (ruleList == null || ruleList.isEmpty()) {
            throw new IllegalConfigurationException("任务未配置");
        }
        for (MetricData metricData : metricDataList) {
            for (Rule rule : ruleList) {
                if (rule.getType().equals("实时")) {
                    if (metricData.getMetric().equals(rule.getIndicatorName())) {
                        boolean status = Comparator.compare(rule.getSymbol(), BigDecimal.valueOf(metricData.getValue()), BigDecimal.valueOf(rule.getParameter()));
                        if (status) { // 要告警
                            generateWarnRecord(metricDataList, rule, metricData.getValue());
                        }
                    }
                }
            }
        }
    }


    @Override
    @Scheduled(cron = "*/30 * * * * *")
    public void scheduledCalculate() {
        log.info("定时任务开始执行");
        timeCalculate();
        log.info("定时任务执行结束");
    }

    public void timeCalculate() {
        String ruleJson = stringRedisTemplate.opsForValue().get("rule");
        List<Rule> ruleList = JSON.parseArray(ruleJson, Rule.class);
        if (ruleList == null || ruleList.isEmpty()) {
            throw new IllegalConfigurationException("任务未配置");
        }
        for (Rule rule : ruleList) {
            if (rule.getType().equals("定时")) {
                if (rule.getAggregate() == null || rule.getAggregate().isEmpty()) {
                    long now = getTime();
                    log.info("现在时间:" + now);
                    Set<String> metricDataSet = stringRedisTemplate.opsForZSet().rangeByScore("metricData", now - 30 * 1000, now);
                    if (metricDataSet == null || metricDataSet.isEmpty()) {
                        throw new NullValueException("30 秒内的数据为空");
                    }
                    for (String metricDataListStr : metricDataSet) {
                        List<MetricData> metricDataList = JSON.parseArray(metricDataListStr, MetricData.class);
                        //提取变量
                        Set<String> variables = FormulaCalculator.extractVariables(rule.getIndicatorName());
                        Map<String, BigDecimal> variableMap = new HashMap<>();
                        //获取指标数据
                        for (MetricData metricData : metricDataList) {
                            for (String variable : variables) {
                                if (metricData.getMetric().equals(variable)) {
                                    DecimalFormat df = new DecimalFormat("#.##");
                                    BigDecimal value = new BigDecimal(df.format(metricData.getValue()));
                                    variableMap.put(variable, value);
                                }
                            }
                        }
                        String formula = rule.getIndicatorName();
                        BigDecimal result = FormulaCalculator.calculateFormula(formula, variableMap);
                        if (Comparator.compare(rule.getSymbol(), result, BigDecimal.valueOf(rule.getParameter()))) {
                            generateWarnRecord(metricDataList, rule, result.doubleValue());
                        }
                    }
                } else {
                    switch (rule.getAggregate()) {
                        case "sum": {
                            sumCalculate(rule);
                            break;
                        }
                        case "avg": {
                            avgCalculate(rule);
                            break;
                        }
                        case "max": {
                            maxCalculate(rule);
                            break;
                        }
                        case "min": {
                            minCalculate(rule);
                            break;
                        }
                        case "rate": {
                            rateCalculate(rule);
                            break;
                        }
                        default:
                            break;
                    }
                }
            }
        }
    }


    // 计算 主机网卡入口流量过高
    public void rateCalculate(Rule rule) {
        long now = getTime();

        // 获取最新的一条数据
        MetricData latestMetricData = new MetricData();
        Set<String> latestMetricDataStrSet = stringRedisTemplate.opsForZSet().reverseRangeByScore("metricData", 0, now, 0, 1);
        if (latestMetricDataStrSet == null || latestMetricDataStrSet.isEmpty()) {
            throw new NullValueException("最新的数据为空");
        }
        for (String latestMetricDataStr : latestMetricDataStrSet) {
            List<MetricData> metricDataList = JSON.parseArray(latestMetricDataStr, MetricData.class);
            for (MetricData metricData : metricDataList) {
                if (metricData.getMetric().equals(rule.getIndicatorName())) {
                    latestMetricData = metricData;
                }
            }
        }
        //获取一分钟之前的那一条数据
        MetricData previousMetricData = new MetricData();
        Set<String> previousMetricDataStrSet = stringRedisTemplate.opsForZSet().rangeByScore("metricData", now - rule.getAggregateTime() * 1000L, now, 0, 1);
        if (previousMetricDataStrSet == null || previousMetricDataStrSet.isEmpty()) {
            throw new NullValueException("一分钟之前的数据为空");
        }
        List<MetricData> metricDataList1 = new ArrayList<>();
        for (String previousMetricDataStr : previousMetricDataStrSet) {
            List<MetricData> metricDataList = JSON.parseArray(previousMetricDataStr, MetricData.class);
            metricDataList1 = metricDataList;
            for (MetricData metricData : metricDataList) {
                if (metricData.getMetric().equals(rule.getIndicatorName())) {
                    previousMetricData = metricData;
                }
            }
        }
//        Double value = latestMetricData.getValue() - previousMetricData.getValue();
//        Long seconds = ChronoUnit.SECONDS.between(previousMetricData.getTimestamp(), latestMetricData.getTimestamp());
//        value = value / 1024 / 1024 / seconds;
        DecimalFormat df = new DecimalFormat("#.##");
        BigDecimal value = new BigDecimal(df.format(latestMetricData.getValue() - previousMetricData.getValue()));

        long milliseconds = ChronoUnit.MILLIS.between(previousMetricData.getTimestamp(), latestMetricData.getTimestamp());
        long seconds = milliseconds / 1000; // 将毫秒转换为秒
        // 确保秒数不为零，避免除以零异常
        if (seconds == 0) {
            throw new ArithmeticException("秒数不能为零");
        }
        // 计算结果
        BigDecimal divisor = BigDecimal.valueOf(1024 * 1024 * seconds);
        BigDecimal result = value.divide(divisor);
        if (Comparator.compare(rule.getSymbol(), result, BigDecimal.valueOf(rule.getParameter()))) {
            generateWarnRecord(metricDataList1, rule, result.doubleValue());
        }
    }


    public void sumCalculate(Rule rule) {
        List<MetricData> metricDataList = getMetricDataList(rule.getAggregateTime() * 1000L);
        BigDecimal sum = BigDecimal.ZERO;
        for (MetricData metricData : metricDataList) {
            if (metricData.getMetric().equals(rule.getIndicatorName())) {
                DecimalFormat df = new DecimalFormat("#.##");
                BigDecimal value = new BigDecimal(df.format(metricData.getValue()));
                sum = sum.add(value);
            }
        }
        BigDecimal result = sum.divide(BigDecimal.valueOf(1024 * 1024), BigDecimal.ROUND_HALF_UP);
        log.info("sum:" + result);
        if (Comparator.compare(rule.getSymbol(), result, BigDecimal.valueOf(rule.getParameter()))) {
            generateWarnRecord(metricDataList, rule, result.doubleValue());
        }
    }


    public void avgCalculate(Rule rule) {
        List<MetricData> metricDataList = getMetricDataList(rule.getAggregateTime() * 1000L);
        BigDecimal sum = BigDecimal.ZERO;
        int count = 0;
        for (MetricData metricData : metricDataList) {
            if (metricData.getMetric().equals(rule.getIndicatorName())) {
                DecimalFormat df = new DecimalFormat("#.##");
                BigDecimal value = new BigDecimal(df.format(metricData.getValue()));
                sum = sum.add(value);
                count++;
            }
        }
        BigDecimal result = sum.divide(BigDecimal.valueOf((long) count * 1024 * 1024), BigDecimal.ROUND_HALF_UP);
        log.info("avg:" + result);
        if (Comparator.compare(rule.getSymbol(), result, BigDecimal.valueOf(rule.getParameter()))) {
            generateWarnRecord(metricDataList, rule, result.doubleValue());
        }
    }


    public void maxCalculate(Rule rule) {
        List<MetricData> metricDataList = getMetricDataList(rule.getAggregateTime() * 1000L);
        BigDecimal max = BigDecimal.ZERO;
        for (MetricData metricData : metricDataList) {
            if (metricData.getMetric().equals(rule.getIndicatorName())) {
                if (metricData.getValue() > max.doubleValue()) {
                    max = BigDecimal.valueOf(metricData.getValue());
                }
            }
        }
        BigDecimal result = max.divide(BigDecimal.valueOf(1024 * 1024), BigDecimal.ROUND_HALF_UP);
        log.info("max:" + result);
        if (Comparator.compare(rule.getSymbol(), result, BigDecimal.valueOf(rule.getParameter()))) {
            generateWarnRecord(metricDataList, rule, result.doubleValue());
        }
    }


    public void minCalculate(Rule rule) {
        List<MetricData> metricDataList = getMetricDataList(rule.getAggregateTime() * 1000L);
        BigDecimal min = BigDecimal.valueOf(Double.MAX_VALUE);
        for (MetricData metricData : metricDataList) {
            if (metricData.getMetric().equals(rule.getIndicatorName())) {
                if (metricData.getValue() < min.doubleValue()) {
                    min = BigDecimal.valueOf(metricData.getValue());
                }
            }
        }
        BigDecimal result = min.divide(BigDecimal.valueOf(1024 * 1024), BigDecimal.ROUND_HALF_UP);
        log.info("min:" + result);
        if (Comparator.compare(rule.getSymbol(), result, BigDecimal.valueOf(rule.getParameter()))) {
            generateWarnRecord(metricDataList, rule, result.doubleValue());
        }
    }


    @PostConstruct
    private void setSystemTime() {
        new Thread(() -> {
            if ("bySystemCurrentTime".equals(mode)) {
                offset = 0;
            } else if ("byJsonDataTime".equals(mode)) {
                while (true) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 取测试数据中最早的时间作为系统时间
                    Set<String> metricDataSet = stringRedisTemplate.opsForZSet().rangeByScore("metricData", 0, System.currentTimeMillis(), 0, 1);
                    if (metricDataSet == null || metricDataSet.isEmpty()) {
                        log.warn("数据为空，无法获取系统时间");
                        continue;
                    }
                    for (String metricDataListStr : metricDataSet) {
                        List<MetricData> metricDataList = JSON.parseArray(metricDataListStr, MetricData.class);
                        //取出需要的指标数据
                        for (MetricData metricData : metricDataList) {
                            // 转换为时间戳
                            long systemTime = metricData.getTimestamp().atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
                            long NowTime = System.currentTimeMillis();
                            //测试数据的开始时间是 2024-04-17 13:55:00
                            long testStartTime = 1713333300000L;
                            offset = systemTime - NowTime;
//                            offset = testStartTime - NowTime;
                            return;
                        }
                    }
                }
            } else {
                throw new IllegalConfigurationException("不存在的模式配置");
            }
        }).start();
    }

    public long getTime() {
        return System.currentTimeMillis() + offset;
    }

    // 删除redis zset 旧数据
//    @Scheduled(cron = "0 */1 * ? * *")
    public void deleteMetricData() {
        long now = getTime();
        log.info("现在时间:" + now);
        // 删除1分钟之前的数据
        Set<String> oldMetricDataStrSet = stringRedisTemplate.opsForZSet().rangeByScore("metricData", Double.NEGATIVE_INFINITY, now - 61 * 1000);
        if (oldMetricDataStrSet != null && !oldMetricDataStrSet.isEmpty()) {
            for (String oldMetricDataStr : oldMetricDataStrSet) {
                // 删除这些数据
                stringRedisTemplate.opsForZSet().remove("metricData", oldMetricDataStr);
                log.info("删除redis zset 旧数据:" + oldMetricDataStr);
            }
        }
    }

    public List<MetricData> getMetricDataList(long offset) {
        long endTime = getTime();
        long startTime = endTime - offset;

        Set<String> metricDataSet = stringRedisTemplate.opsForZSet().rangeByScore("metricData", startTime, endTime);

        if (metricDataSet == null || metricDataSet.isEmpty()) {
            throw new NullValueException(startTime + "到" + endTime + "内的数据为空");
        }
        List<MetricData> resultList = new ArrayList<>();
        for (String metricDataListStr : metricDataSet) {
            List<MetricData> metricDataList = JSON.parseArray(metricDataListStr, MetricData.class);
            resultList.addAll(metricDataList);
        }
        return resultList;
    }

    public void generateWarnRecord(List<MetricData> metricDataList, Rule rule, Double value) {
        if (rule.getType().equals("实时")) {
            log.error("实时计算告警:" + rule.getName() + "," + value + rule.getSymbol() + rule.getParameter());
        } else if (rule.getType().equals("定时")) {
            log.error("定时计算告警:" + rule.getName() + "," + value + rule.getSymbol() + rule.getParameter());
        }

        WarnRecord warnRecord = new WarnRecord();
        warnRecord.setHostname(metricDataList.get(0).getHostname());
        warnRecord.setInstanceId(metricDataList.get(0).getInstanceId());
        warnRecord.setType(rule.getType());
        warnRecord.setIndicatorType(rule.getName());

        if (rule.getAggregate() == null || rule.getAggregate().isEmpty()) {
            warnRecord.setTriggerCondition(rule.getIndicatorName() + rule.getSymbol() + rule.getParameter());
        } else {
            warnRecord.setTriggerCondition(rule.getAggregate() + "(" + rule.getIndicatorName() + "[" + rule.getAggregateTime() + "s]" + ")" + rule.getSymbol() + rule.getParameter());
        }
        warnRecord.setTriggerValue(value);
        warnRecord.setOccurTime(metricDataList.get(0).getTimestamp());
        warnRecord.setDealTime(LocalDateTime.now());
        warnRecord.setStatus("未读");
        save(warnRecord);
    }


    /**
     * 标记告警记录为已读
     *
     * @param id 告警记录id
     */
    @Override
    public void readWarnRecord(Long id) {
        WarnRecord warnRecord = getById(id);
        warnRecord.setStatus("已读");
        updateById(warnRecord);
    }

    /**
     * 分页查询告警记录
     *
     * @param pageQueryDTO 分页查询参数
     * @return 分页结果
     */
    @Override
    public PageResult pageWarnRecord(PageQueryDTO pageQueryDTO) {
        IPage page = new Page(pageQueryDTO.getPage(), pageQueryDTO.getPageSize());
        LambdaQueryWrapper<WarnRecord> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.orderByDesc(WarnRecord::getDealTime);
        page = page(page, lambdaQueryWrapper);
        PageResult pageResult = new PageResult();
        pageResult.setTotal(page.getTotal());
        pageResult.setRecords(page.getRecords());
        return pageResult;
    }


    @Override
    public List<WarnRecordVO> queryAlertByTime(RequestVO requestVO) {
        LocalDateTime startTime = convertStringToDatetime(requestVO.getStart());
        LocalDateTime endTime = convertStringToDatetime(requestVO.getEnd());

        QueryWrapper<WarnRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("occur_time", startTime, endTime);
        List<WarnRecord> warnRecordList = warnRecordMapper.selectList(queryWrapper);

        return warnRecordList.stream()
                .map(warnRecordConverter::convertWarnRecordToWarnRecordVO)
                .collect(Collectors.toList());
    }

    //把String类型的时间转化成Datetime类型
    public static LocalDateTime convertStringToDatetime(String time) {
        final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        try {
            return LocalDateTime.parse(time, FORMATTER);
        } catch (DateTimeParseException e) {
            // 处理解析异常，例如记录日志或抛出自定义异常
            throw new IllegalArgumentException("非法的时间格式, 应为 'yyyy-MM-dd HH:mm:ss'", e);
        }
    }

    //把WarnRecord转化为WarnRecordVO
    public static class warnRecordConverter {
        public static WarnRecordVO convertWarnRecordToWarnRecordVO(WarnRecord warnRecord) {
            WarnRecordVO warnRecordVO = new WarnRecordVO();
            warnRecordVO.setCondition(warnRecord.getTriggerCondition());
            warnRecordVO.setTimestamp((int) warnRecord.getOccurTime().toEpochSecond(ZoneOffset.UTC));
            warnRecordVO.setValue(warnRecord.getTriggerValue());
            return warnRecordVO;
        }
    }
}
